package com.cskaoyan.wordmemorize.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.common.listener.FillTopicExcelCheckListener;
import com.cskaoyan.wordmemorize.common.listener.FillTopicExcelListener;
import com.cskaoyan.wordmemorize.common.listener.SingleTopicExcelCheckListener;
import com.cskaoyan.wordmemorize.common.listener.SingleTopicExcelListener;
import com.cskaoyan.wordmemorize.converter.TopicConverter;
import com.cskaoyan.wordmemorize.converter.VocConverter;
import com.cskaoyan.wordmemorize.dao.entity.ReviewSubTopicDO;
import com.cskaoyan.wordmemorize.dao.entity.ReviewTopicDO;
import com.cskaoyan.wordmemorize.dao.entity.TopicOptionDO;
import com.cskaoyan.wordmemorize.dao.entity.VocDO;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.*;
import com.cskaoyan.wordmemorize.request.ReviewSubTopicCommand;
import com.cskaoyan.wordmemorize.request.TopicCommand;
import com.cskaoyan.wordmemorize.request.TopicOptionCommand;
import com.cskaoyan.wordmemorize.request.VocPageRequest;
import com.cskaoyan.wordmemorize.service.ReviewSubTopicService;
import com.cskaoyan.wordmemorize.service.ReviewTopicOptionService;
import com.cskaoyan.wordmemorize.service.ReviewTopicService;
import com.cskaoyan.wordmemorize.service.TopicService;
import jakarta.annotation.Resource;
import org.apache.ibatis.executor.BatchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class TopicServiceImpl implements TopicService {

    /*
        ReviewTopicMapper  题目的mapper
        ReviewTopicOptionMapper 单选题答案和填空题干扰项的mapper
        ReviewSubTopicMapper  填空题的mapper
     */

    @Resource
    ReviewTopicMapper reviewTopicMapper;
    @Resource
    ReviewTopicOptionMapper reviewTopicOptionMapper;
    @Resource
    ReviewSubTopicMapper reviewSubTopicMapper;
    @Resource
    TopicConverter topicConverter;

    @Autowired
    VocMapper vocMapper;

    @Autowired
    VocConverter vocConverter;


    /*
        业务接口
     */

    @Autowired
    ReviewTopicService reviewTopicService;
    @Autowired
    ReviewTopicOptionService reviewTopicOptionService;
    @Autowired
    ReviewSubTopicService reviewSubTopicService;

    @Override
    public VocDTO checkWord(String word) {

        LambdaQueryWrapper<VocDO> vocDOWrapper = new LambdaQueryWrapper<>();
        vocDOWrapper.eq(VocDO::getContent, word);

        VocDO vocDO = vocMapper.selectOne(vocDOWrapper);
        if (vocDO ==  null) {
            throw new RuntimeException(word + "单词不存在");
        }

        return vocConverter.vocDO2DTO(vocDO);
    }

    @Override
    public PageDTO<ReviewTopicDTO> getSingleChoicePage(VocPageRequest vocPageRequest) {

        List<ReviewTopicDO> reviewTopicDOS = reviewTopicMapper.getSingleChoicePage(vocPageRequest);
        QueryWrapper<ReviewTopicDO> topicDOQueryWrapper = new QueryWrapper<>();
        topicDOQueryWrapper.eq("topic_type",vocPageRequest.getTopicType());
        Long size = Long.valueOf(reviewTopicMapper.selectList(topicDOQueryWrapper).size());
        PageDTO<ReviewTopicDTO> reviewTopicDTOPageDTO = topicConverter.topicDOPage2PageDTO(reviewTopicDOS, size);

        return reviewTopicDTOPageDTO;
    }

    @Override
    public PageDTO<ReviewTopicDTO> getFillWordPage(VocPageRequest vocPageRequest) {
        // 1. 先拿到所有的 填空题
        List<ReviewTopicDO> reviewTopicDOS = reviewTopicMapper.getFillWordPage(vocPageRequest);
        QueryWrapper<ReviewTopicDO> topicDOQueryWrapper = new QueryWrapper<>();
        topicDOQueryWrapper.eq("topic_type",vocPageRequest.getTopicType());
        Long size = Long.valueOf(reviewTopicMapper.selectList(topicDOQueryWrapper).size());

        PageDTO<ReviewTopicDTO> reviewTopicDTOPageDTO = topicConverter.topicDOPage2PageDTO(reviewTopicDOS, size);

        // 2. 根据填空题的id 去查对应的填空题的答案
        reviewTopicDTOPageDTO.getPageList().forEach(reviewTopicDTO -> {
            QueryWrapper<ReviewSubTopicDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("tid", reviewTopicDTO.getId());
            List<ReviewSubTopicDO> reviewSubTopicDOS = reviewSubTopicMapper.selectList(queryWrapper);
            reviewTopicDTO.setSubTopics(topicConverter.reviewSubTopicDOs2DTOs(reviewSubTopicDOS));
        });
        return reviewTopicDTOPageDTO;
    }

    @Override
    @Transactional
    public void addTopic(TopicCommand command) {
        // 1. 添加， 分为几类， 题目的添加 ， 单选就是全部options, 填空则是sub 和options
        ReviewTopicDO reviewTopicDO = topicConverter.command2TopicDO(command);
        if (command.getTopicType() == 1){
            // 1. 先将 前端参数转换为DO对象，再将前端参数里面的答案转换为Do对象
            List<TopicOptionCommand> options = command.getOptions();
            List<TopicOptionDO> topicOptionDOS = topicConverter.commands2ReviewTopicOptionDOs(options);
            reviewTopicMapper.insert(reviewTopicDO);
            topicOptionDOS.forEach(topicOptionDO -> {
                topicOptionDO.setTid(reviewTopicDO.getId());
                reviewTopicOptionMapper.insert(topicOptionDO);
            });

        }else if (command.getTopicType() == 2){
            StringBuffer stringBuffer = new StringBuffer();
            // 2. 总体和 1 一样
            List<ReviewSubTopicCommand> subTopics = command.getSubTopics();
            List<ReviewSubTopicDO> reviewSubTopicDOS = topicConverter.commands2ReviewSubTopicDOs(subTopics);

            reviewSubTopicDOS.forEach(reviewSubTopicDO -> {
                // 3. 多选题目需要自己拼接，利用小题，拿到题目，进行拼接
                String topicItem = reviewSubTopicDO.getTopicItem();
                stringBuffer.append(topicItem + "\n");

            });
            // 设置题目
            reviewTopicDO.setTopic(stringBuffer.toString());
            reviewTopicMapper.insert(reviewTopicDO);
            reviewSubTopicDOS.forEach(reviewSubTopicDO -> {
                // 3. 多选题目需要自己拼接，利用小题，拿到题目，进行拼接
                reviewSubTopicDO.setTid(reviewTopicDO.getId());
                reviewSubTopicMapper.insert(reviewSubTopicDO);

            });
            List<TopicOptionCommand> options = command.getOptions();
            if (options != null) {

                List<TopicOptionDO> topicOptionDOS = topicConverter.commands2ReviewTopicOptionDOs(options);
                topicOptionDOS.forEach(topicOptionDO -> {
                    topicOptionDO.setTid(reviewTopicDO.getId());
                    reviewTopicOptionMapper.insert(topicOptionDO);
                });
            }
        }


    }

    @Override
    public ReviewTopicDTO getTopic(Long topicId) {
        ReviewTopicDO reviewTopicDO = reviewTopicMapper.selectById(topicId);
        ReviewTopicDTO reviewTopicDTO = topicConverter.reviewTopicDO2DTO(reviewTopicDO);
        // 1. 拿到 题目后根据相应的type 来进行区分，当选和填空返回的不一样，sql也不一样
        if (reviewTopicDO.getTopicType() == 1) {
            // 2. 如果是单选 ，只需要联两张表就行
            LambdaQueryWrapper<TopicOptionDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TopicOptionDO::getTid, reviewTopicDO.getId());
            List<TopicOptionDO> topicOptionDOS = reviewTopicOptionMapper.selectList(queryWrapper);
            List<TopicOptionDTO> topicOptionDTOS = topicConverter.topicOptionDOs2DTOs(topicOptionDOS);
            reviewTopicDTO.setOptions(topicOptionDTOS);
        }else if (reviewTopicDO.getTopicType() == 2) {
            // 3. 如果是填空题，需要联三张表
            // 4. 三个正确选项的查询
            LambdaQueryWrapper<ReviewSubTopicDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ReviewSubTopicDO::getTid, reviewTopicDO.getId());
            List<ReviewSubTopicDO> reviewSubTopicDOS = reviewSubTopicMapper.selectList(queryWrapper);

            // 数据库对应的 并没有多选的赋值，转换为DTO在赋值
            List<ReviewSubTopicDTO> reviewSubTopicDTOS = topicConverter.reviewSubTopicDOs2DTOs(reviewSubTopicDOS);
            reviewTopicDTO.setSubTopics(reviewSubTopicDTOS);
            // 6. 一个干扰选项的查询
            LambdaQueryWrapper<TopicOptionDO> optionDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
            optionDOLambdaQueryWrapper.eq(TopicOptionDO::getTid, reviewTopicDO.getId())
                    .eq(TopicOptionDO::getIsAnswer, 0);
            List<TopicOptionDO> topicOptionDOS = reviewTopicOptionMapper.selectList(optionDOLambdaQueryWrapper);
            List<TopicOptionDTO> topicOptionDTOS = topicConverter.topicOptionDOs2DTOs(topicOptionDOS);
            reviewTopicDTO.setOptions(topicOptionDTOS);
        }


        return reviewTopicDTO;
    }

    @Override
    @Transactional
    public void updateTopic(TopicCommand command) {
        //  前端逻辑有点问题 ，后面修改的时候可以自己根据拿到的值 循环匹配
        //  ， 然后自己设置isAnswer的值
        /*
            1. 拿到 topicId ，根据id 来确定题目
            2. 单选就 获取command里面的options ，然后根据 topicId 与tid 确定相应选项，
                再根据optionNumber,也可以是option的id， 来具体定位每个选项进行修改
            3. 多选题，大致思路一样，option只有一个，可以直接用topicId定位就行，以后有多个再改
                subTopic的查找和 单选的option一样
            4. 找到后对每个部分进行 更新 ，然后插入即可
         */
        Long topicId = Long.parseLong(command.getId());
        ReviewTopicDO reviewTopicDO = reviewTopicMapper.selectById(topicId);
        if (command.getTopicType() == 1){
            // 对题目进行更新
            reviewTopicDO.setTopic(command.getTopic());
            reviewTopicDO.setVocabulary(command.getVocabulary());
            if (command.getVocId() != null)
                reviewTopicDO.setVocId(Long.parseLong(command.getVocId()));

            // 拿到答案 进行后续匹配
            String answer = command.getVocabulary();
            // 对选项进行更新
            List<TopicOptionCommand> options = command.getOptions();
            List<TopicOptionDO> newTopicOptionDOS = topicConverter.commands2ReviewTopicOptionDOs(options);
//            List<Long> idList = newTopicOptionDOS.stream().map(topicOptionDO -> topicOptionDO.getId()).collect(Collectors.toList());

            // 循环根据id 更新 , id 不为空 ，则进行更新 ，为空说明是添加
            newTopicOptionDOS.forEach(newTopicOptionDO -> {
                if (newTopicOptionDO.getId() == null){
                    newTopicOptionDO.setTid(topicId);
                    reviewTopicOptionMapper.insert(newTopicOptionDO);
                }else {
                    if (answer.equals(newTopicOptionDO.getItem())){
                        newTopicOptionDO.setIsAnswer(1);
                    }else {
                        newTopicOptionDO.setIsAnswer(0);
                    }
                    LambdaUpdateWrapper<TopicOptionDO> topicOptionUpdateWrapper = new LambdaUpdateWrapper<>();
                    topicOptionUpdateWrapper.eq(TopicOptionDO::getTid,topicId)
                            .eq(TopicOptionDO::getId,newTopicOptionDO.getId());
                    reviewTopicOptionMapper.update(newTopicOptionDO,topicOptionUpdateWrapper);
                }
            });

            // 更新 review_topic表
            reviewTopicMapper.updateById(reviewTopicDO);
        } else if (command.getTopicType() == 2) {
            StringBuffer topicStringBuffer = new StringBuffer();
            // 多选题目要自己拼接, 要先获取subTopic子集
            reviewTopicDO.setVocabulary(command.getVocabulary());
            List<ReviewSubTopicCommand> subTopics = command.getSubTopics();
            List<ReviewSubTopicDO> reviewSubTopicDOS = topicConverter.commands2ReviewSubTopicDOs(subTopics);
//            List<Long> idList = reviewSubTopicDOS.stream().map(reviewSubTopicDO -> reviewSubTopicDO.getId()).collect(Collectors.toList());


            reviewSubTopicDOS.forEach(reviewSubTopicDO -> {
                String topicItem = reviewSubTopicDO.getTopicItem();
                topicStringBuffer.append(topicItem + "\n");
                if (reviewSubTopicDO.getId() == null){
                    reviewSubTopicDO.setTid(topicId);
                    reviewSubTopicMapper.insert(reviewSubTopicDO);
                }else {
                    LambdaUpdateWrapper<ReviewSubTopicDO> updateWrapper = new LambdaUpdateWrapper<>();
                    updateWrapper.eq(ReviewSubTopicDO::getTid,topicId)
                            .eq(ReviewSubTopicDO::getId,reviewSubTopicDO.getId());
                    reviewSubTopicMapper.update(reviewSubTopicDO,updateWrapper);
                }
            });
            // 将拼接完成的题目 赋值，revie_topic 的转换基本完成
            reviewTopicDO.setTopic(topicStringBuffer.toString());

            // option 的转换
            List<TopicOptionCommand> options = command.getOptions();
            // options 不为null 证明有要更新的值
            if (options != null){
                List<TopicOptionDO> topicOptionDOS = topicConverter.commands2ReviewTopicOptionDOs(options);
                TopicOptionDO topicOptionDO = topicOptionDOS.get(0);
                Long optionId = topicOptionDO.getId();
                LambdaUpdateWrapper<TopicOptionDO> optionUpdateWrapper = new LambdaUpdateWrapper<>();
                // optionId为空 证明这个干扰选项是插入的， 否则更新
                if (optionId == null){
                    topicOptionDO.setTid(topicId);
                    reviewTopicOptionMapper.insert(topicOptionDO);
                }else {
                    optionUpdateWrapper.eq(TopicOptionDO::getTid,topicId)
                            .eq(TopicOptionDO::getId,optionId);
                    reviewTopicOptionMapper.update(topicOptionDO,optionUpdateWrapper);
                }
            }

            // 更新 review_topic表
            reviewTopicMapper.updateById(reviewTopicDO);


        }

    }

    @Override
    @Transactional
    public DeleteTopicOperateDTO deleteTopic(Long topicId, Integer topicType) {
        ReviewTopicDO reviewTopicDO = reviewTopicMapper.selectById(topicId);

        if (reviewTopicDO == null){
            throw new BusinessException("要删除的题目不存在",ResultCodeEnum.FAIL.getCode());
        }

        DeleteTopicOperateDTO deleteTopicOperateDTO = topicConverter.topicDO2DeleteTopicDTO(reviewTopicDO);

        // 确定数据库有单词后 ，删单词 及对应的表的数据
        // 1. 如果是单选 ， 就删除 options 里面的数据 ，
        //    如果删除的是正确答案 ，那么vocId，以及vocabulary被修改为null
        // 2. 如果是多选 ，还需要重新拼接题目 ，答案直接更新
        if (topicType == 1){
            LambdaQueryWrapper<TopicOptionDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TopicOptionDO::getTid,topicId);
            List<TopicOptionDO> topicOptionDOS = reviewTopicOptionMapper.selectList(queryWrapper);
            if (topicOptionDOS != null){
                topicOptionDOS.forEach(topicOptionDO -> deleteTopicOption(topicOptionDO.getId()));
            }
        } else if (topicType == 2) {
            LambdaQueryWrapper<ReviewSubTopicDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ReviewSubTopicDO::getTid, topicId);
            List<ReviewSubTopicDO> reviewSubTopicDOS = reviewSubTopicMapper.selectList(queryWrapper);
            if (reviewSubTopicDOS != null){
                reviewSubTopicDOS.forEach(reviewSubTopicDO -> deleteSubTopic(reviewSubTopicDO.getId()));
            }
            LambdaQueryWrapper<TopicOptionDO> optionQueryWrapper = new LambdaQueryWrapper<>();
            optionQueryWrapper.eq(TopicOptionDO::getTid,topicId);
            TopicOptionDO topicOptionDO = reviewTopicOptionMapper.selectOne(optionQueryWrapper);
            if (topicOptionDO != null){
                deleteTopicOption(topicOptionDO.getId());
            }
        }
        reviewTopicMapper.deleteById(topicId);

        return deleteTopicOperateDTO;
    }

    @Override
    public void topicExcelValidate(String type, MultipartFile file) throws IOException {
        if ("single".equals(type)){
            EasyExcel.read(file.getInputStream(), SingleTopicExcelDTO.class, new SingleTopicExcelCheckListener(vocMapper))
                    .sheet()
                    .doRead();


        } else if ("fill".equals(type)) {
            EasyExcel.read(file.getInputStream(), FillTopicExcelDTO.class, new FillTopicExcelCheckListener(vocMapper))
                    .sheet()
                    .doRead();

        }
    }

    @Override
    public ExcelAddOperateDTO addSinleChoiceExcel(MultipartFile file, Long employeeId) throws IOException {
        List<Object> list = EasyExcel.read(file.getInputStream(), SingleTopicExcelDTO.class, new SingleTopicExcelListener(vocMapper, reviewTopicService, reviewTopicOptionService, topicConverter))
                .sheet()
                .doReadSync();
        ExcelAddOperateDTO excelAddOperateDTO = new ExcelAddOperateDTO();
        excelAddOperateDTO.setTotalCount(list.size());
        String filename = file.getOriginalFilename();
        excelAddOperateDTO.setFileName(filename);

        return excelAddOperateDTO;
    }

    @Override
    public ExcelAddOperateDTO addFillWordExcel(MultipartFile file, Long employeeId) throws IOException {
        List<Object> list = EasyExcel.read(file.getInputStream(), FillTopicExcelDTO.class, new FillTopicExcelListener(vocMapper, reviewTopicService, reviewTopicOptionService, reviewSubTopicService))
                .sheet()
                .doReadSync();
        ExcelAddOperateDTO excelAddOperateDTO = new ExcelAddOperateDTO();
        excelAddOperateDTO.setTotalCount(list.size());
        String filename = file.getOriginalFilename();
        excelAddOperateDTO.setFileName(filename);

        return excelAddOperateDTO;
    }


    /**
     * 填空的删除
     * @param id
     * @return
     */
    @Override
    public DeleteSubTopicOperateDTO deleteSubTopic(Long id) {

        // 多选 1. 如果直接删小题，直接删就行
        // 2. 如果删的是与小题绑定的单词， 单词和小题都要删
        ReviewSubTopicDO reviewSubTopicDO = reviewSubTopicMapper.selectById(id);
        DeleteSubTopicOperateDTO deleteSubTopicDTO = topicConverter.toDeleteSubTopicDTO(reviewSubTopicDO);
        reviewSubTopicMapper.deleteById(id);
        return deleteSubTopicDTO;
    }


    /**
     * 单选和干扰项的删除
     * @param id
     * @return
     */
    @Override
    @Transactional
    public DeleteTopicOptionOperateDTO deleteTopicOption(Long id) {
        TopicOptionDO topicOptionDO = reviewTopicOptionMapper.selectById(id);
        ReviewTopicDO reviewTopicDO = reviewTopicMapper.selectById(topicOptionDO.getTid());

        if (topicOptionDO == null){
            throw new BusinessException("查无此选项",ResultCodeEnum.FAIL.getCode());
        }
        String topic = reviewTopicDO.getTopic();
        DeleteTopicOptionOperateDTO deleteTopicOptionDTO = topicConverter.toDeleteTopicOptionDTO(topicOptionDO,topic);
        //  删除逻辑 ，先和题目匹配 ， 如果不是题目 就正常删 ，
        //  如果是题目答案，那么vocId，以及vocabulary被修改为null
        if(topicOptionDO.getItem().equals(topic)){
            reviewTopicMapper.updateReviewTopicWithNullById(reviewTopicDO);
        }
        reviewTopicOptionMapper.deleteById(id);
        return deleteTopicOptionDTO;
    }
}
